﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;

using Windows.ApplicationModel.Resources;
using Windows.Foundation;
using Windows.UI.StartScreen;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

using SeriesManager.Enum;
using SeriesManager.Model;
using SeriesManager.Extension;
using SeriesManager.Model.Banner;
using System.Threading.Tasks;
using Windows.UI.Xaml.Data;
using System.Runtime.Serialization;
using SeriesManager.Core;
using WinRTFramework.Common;
using WinRTFramework.Interfaces.AppStates;

namespace SeriesManager.ViewModel
{
    public class SeriesViewModel : ViewModelBase, ISupportTransientState
    {
        #region Properties

        private bool tileExists;
        private SeriesModel viewSource;
        private FanArtBannerModel randomFanArt;

        public ICollectionView View
        {
            get;
            private set;
        }

        public IEnumerable<IEnumerable<string>> MetaData
        {
            get
            {
                if (this.ViewSource == null) return null;
                var res = new ResourceLoader();
                int seasons = this.ViewSource.Seasons.Count;
                int episodes = this.ViewSource.Count;

                var shortMetaData = new List<string>();
                if (this.ViewSource.FirstAired.Date.CompareTo(DateTime.MinValue) != 0)
                {
                    shortMetaData.Add(this.ViewSource.FirstAired.Year.ToString());
                }

                if (this.ViewSource.Rating != 0.1)
                {
                    shortMetaData.Add(res.GetString("Rating") + ": " + this.ViewSource.Rating.ToString("0.0"));
                }

                if (this.ViewSource.Runtime > 0)
                {
                    shortMetaData.Add(res.GetString("Duration") + ": " + this.ViewSource.Runtime.ToString() + " " + res.GetString("MinutesAbbreviation"));
                }

                if (this.ViewSource.AirTime.Ticks > 0 && this.ViewSource.Active)
                {
                    shortMetaData.Add(CultureInfo.CurrentCulture.DateTimeFormat.GetDayName((System.DayOfWeek)this.ViewSource.AirDay) + " " + new DateTime(this.ViewSource.AirTime.Ticks).ToString(CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern));
                }

                if (!this.ViewSource.Active)
                {
                    shortMetaData.Add(new ResourceLoader().GetString("NotRunning"));
                }

                shortMetaData.Add(seasons.ToString() + " " + (seasons == 1 ? res.GetString("Season") : res.GetString("Seasons")));
                shortMetaData.Add(episodes.ToString() + " " + (episodes == 1 ? res.GetString("Episode") : res.GetString("Episodes")));

                return new List<IEnumerable<string>>
                {
                    shortMetaData,
                    this.ViewSource.Genre
                };
            }
        }

        public SeriesModel ViewSource
        {
            get { return this.viewSource; }
            private set
            {
                base.SetProperty<SeriesModel>(ref this.viewSource, value);

                base.OnPropertyChanged<EpisodeModel>(() => this.LatestUnwatched);
                base.OnPropertyChanged<EpisodeModel>(() => this.NextUnAired);
                base.OnPropertyChanged<IEnumerable<IEnumerable<string>>>(() => this.MetaData);
                this.SubscriptionCommand.NotifyCanExecuteChanged();
                this.StateCommand.NotifyCanExecuteChanged();
                this.MediaCommand.NotifyCanExecuteChanged();
                this.TileCommand.NotifyCanExecuteChanged();
                this.UnAiredCommand.NotifyCanExecuteChanged();
                this.ActorCommand.NotifyCanExecuteChanged();
                this.LatestUnwatchedCommand.NotifyCanExecuteChanged();
                this.NextUnAiredCommand.NotifyCanExecuteChanged();
                this.WatchStateCommand.NotifyCanExecuteChanged();
                this.UnwatchStateCommand.NotifyCanExecuteChanged();
                this.SelectionAllCommand.NotifyCanExecuteChanged();
            }
        }

        void viewSource_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "State")
            {
                this.StateCommand.NotifyCanExecuteChanged();
            }
        }

        public EpisodeModel LatestUnwatched
        {
            get
            {
                return this.ViewSource != null ? this.ViewSource.Unwatched.FirstOrDefault() : null;
            }
        }

        public EpisodeModel NextUnAired
        {
            get
            {
                return this.ViewSource != null ? this.ViewSource.UnAired.FirstOrDefault(S => S.FirstAired.CompareTo(DateTime.MinValue) != 0) : null;
            }
        }

        public bool TileExists
        {
            get 
            { 
                return this.tileExists; 
            }
            private set 
            {
                base.SetProperty<bool>(ref this.tileExists, value);
            }
        }

        public FanArtBannerModel RandomFanArt
        {
            get
            {
                return this.randomFanArt;
            }
            private set
            {
                base.SetProperty<FanArtBannerModel>(ref this.randomFanArt, value);
            }
        }

        public ObservableCollection<SeasonModel> SelectedItems
        { 
            get; 
            private set; 
        }

        #endregion

        #region DelegateCommands

        public DelegateCommand<ItemClickEventArgs> WatchStateCommand
        {
            get;
            private set;
        }

        public DelegateCommand<ItemClickEventArgs> UnwatchStateCommand
        {
            get;
            private set;
        }

        public DelegateCommand<ItemClickEventArgs> SelectionAllCommand
        {
            get;
            private set;
        }

        public DelegateCommand<ItemClickEventArgs> SelectionClearCommand
        {
            get;
            private set;
        }

        public DelegateCommand<ItemClickEventArgs> SeasonCommand
        {
            get;
            private set;
        }

        public DelegateCommand<ItemClickEventArgs> StateCommand
        {
            get;
            private set;
        }

        public DelegateCommand<ItemClickEventArgs> MediaCommand
        {
            get;
            private set;
        }

        public DelegateCommand<FrameworkElement> TileCommand
        {
            get;
            private set;
        }

        public DelegateCommand<ItemClickEventArgs> UnAiredCommand
        {
            get;
            private set;
        }

        public DelegateCommand<ItemClickEventArgs> ActorCommand
        {
            get;
            private set;
        }

        public DelegateCommand<ItemClickEventArgs> LatestUnwatchedCommand
        {
            get;
            private set;
        }

        public DelegateCommand<ItemClickEventArgs> NextUnAiredCommand
        {
            get;
            private set;
        }

        public DelegateCommand<ItemClickEventArgs> SubscriptionCommand
        {
            get;
            private set;
        }

        #endregion

        public SeriesViewModel()
        {
            this.SelectedItems = new ObservableCollection<SeasonModel>();
            this.SelectedItems.CollectionChanged += SelectedItems_CollectionChanged;

            this.SeasonCommand = new DelegateCommand<ItemClickEventArgs>(OnSeasonExecuted);
            this.StateCommand = new DelegateCommand<ItemClickEventArgs>(OnStateExecuted, OnStateCanExecute);
            this.MediaCommand = new DelegateCommand<ItemClickEventArgs>(OnMediaExecuted, OnMediaCanExecute);
            this.TileCommand = new DelegateCommand<FrameworkElement>(OnTileExecuted, OnTileCanExecute);
            this.UnAiredCommand = new DelegateCommand<ItemClickEventArgs>(OnUnAiredExecuted, OnUnAiredCanExecute);
            this.ActorCommand = new DelegateCommand<ItemClickEventArgs>(OnActorExecuted, OnActorCanExecute);
            this.LatestUnwatchedCommand = new DelegateCommand<ItemClickEventArgs>(OnLatestUnwatchedExecuted, OnLatestUnwatchedCanExecute);
            this.WatchStateCommand = new DelegateCommand<ItemClickEventArgs>(OnWatchStateExecuted, OnWatchStateCanExecute);
            this.UnwatchStateCommand = new DelegateCommand<ItemClickEventArgs>(OnUnwatchStateExecuted, OnUnwatchStateCanExecute);
            this.SelectionClearCommand = new DelegateCommand<ItemClickEventArgs>(OnSelectionClearExecuted, OnSelectionClearCanExecute);
            this.SelectionAllCommand = new DelegateCommand<ItemClickEventArgs>(OnSelectionAllExecuted, OnSelectionAllCanExecute);
            this.NextUnAiredCommand = new DelegateCommand<ItemClickEventArgs>(OnNextUnAiredExecuted, OnNextUnAiredCanExecute);
            this.SubscriptionCommand = new DelegateCommand<ItemClickEventArgs>(OnSubscriptionExecuted, OnSubscriptionCanExecute);
        }

        #region DelegateProperties

        private void OnSubscriptionExecuted(ItemClickEventArgs e)
        {
            this.ViewSource.Subscription = !this.ViewSource.Subscription;

            this.TileCommand.NotifyCanExecuteChanged();
            this.StateCommand.NotifyCanExecuteChanged();
            this.NextUnAiredCommand.NotifyCanExecuteChanged();
            this.LatestUnwatchedCommand.NotifyCanExecuteChanged();
        }

        private bool OnSubscriptionCanExecute(ItemClickEventArgs e)
        {
            return this.ViewSource != null;
        }

        private void OnWatchStateExecuted(ItemClickEventArgs e)
        {
            for (int i = 0; i < this.SelectedItems.Count; i++)
            {
                if (this.SelectedItems[i].State == WatchState.Unwatched)
                {
                    this.SelectedItems[i].State = WatchState.Watched;
                    this.SelectedItems.RemoveAt(i);
                    i--;
                }
            }

            InvalidateCommands();
            TileManager.UpdateTile(this.ViewSource.ID, this.ViewSource.Unwatched.Count);
        }

        private bool OnWatchStateCanExecute(ItemClickEventArgs e)
        {
            return this.ViewSource != null && this.ViewSource.Subscription && this.SelectedItems.Any(E => E.State == WatchState.Unwatched);
        }

        private void OnUnwatchStateExecuted(ItemClickEventArgs e)
        {
            for (int i = 0; i < this.SelectedItems.Count; i++)
            {
                if (this.SelectedItems[i].State == WatchState.Watched || (this.SelectedItems[i].State == WatchState.UnAired && this.SelectedItems[i].Watched.Any()))
                {
                    this.SelectedItems[i].State = WatchState.Unwatched;
                    this.SelectedItems.RemoveAt(i);
                    i--;
                }
            }

            InvalidateCommands();
            TileManager.UpdateTile(this.ViewSource.ID, this.ViewSource.Unwatched.Count);
        }

        private bool OnUnwatchStateCanExecute(ItemClickEventArgs e)
        {
            return this.ViewSource != null && this.ViewSource.Subscription && this.SelectedItems.Any(E => E.State == WatchState.Watched || (E.Watched.Any() && E.State == WatchState.UnAired));
        }

        private void OnSelectionClearExecuted(ItemClickEventArgs e)
        {
            this.SelectedItems.Clear();
        }

        private bool OnSelectionClearCanExecute(ItemClickEventArgs e)
        {
            return this.SelectedItems.Any();
        }

        private void OnSelectionAllExecuted(ItemClickEventArgs e)
        {
            foreach (var season in this.ViewSource.Seasons)
            {
                if (!this.SelectedItems.Any(S => S.Number == season.Number))
                {
                    this.SelectedItems.Add(season);
                }
            }
        }

        private bool OnSelectionAllCanExecute(ItemClickEventArgs e)
        {
            return this.SelectedItems != null && this.ViewSource != null && this.ViewSource.Seasons.Any();
        }

        private void OnSeasonExecuted(ItemClickEventArgs e)
        {
            var clickedItem = (SeasonModel)e.ClickedItem;
            base.NavigationService.Navigate<SeasonViewModel>(clickedItem.Number);
        }

        private void OnStateExecuted(ItemClickEventArgs e)
        {
            this.ViewSource.State = this.ViewSource.State.Invert();

            base.OnPropertyChanged<EpisodeModel>(() => this.LatestUnwatched);
            base.OnPropertyChanged<EpisodeModel>(() => this.NextUnAired);
            LatestUnwatchedCommand.NotifyCanExecuteChanged();
            NextUnAiredCommand.NotifyCanExecuteChanged();

            TileManager.UpdateTile(this.ViewSource.ID, this.ViewSource.Unwatched.Count);
        }

        private bool OnStateCanExecute(ItemClickEventArgs e)
        {
            return this.ViewSource != null && this.ViewSource.Subscription && (this.ViewSource.Watched.Any() || this.ViewSource.Unwatched.Any());
        }

        private void OnMediaExecuted(ItemClickEventArgs e)
        {
            base.NavigationService.Navigate<MediaViewModel>();
        }

        private bool OnMediaCanExecute(ItemClickEventArgs e)
        {
            return this.ViewSource != null && this.ViewSource.FanArtBanners.Count > 1;
        }

        private async void OnTileExecuted(FrameworkElement button)
        {
            GeneralTransform buttonTransform = button.TransformToVisual(null);
            Point point = buttonTransform.TransformPoint(new Point());
            var rect = new Rect(point, new Size(button.ActualWidth, button.ActualHeight));

            if (!SecondaryTile.Exists(this.ViewSource.ID.ToString()))
            {
                await this.ViewSource.FanArt.DownloadThumbnail();
                Uri thumb = new Uri(this.ViewSource.FanArt.ThumbnailLocalPath);
                SecondaryTile secondaryTile = new SecondaryTile(this.ViewSource.ID.ToString(), this.ViewSource.Name, this.ViewSource.ID.ToString(), thumb, TileSize.Wide310x150);
                secondaryTile.VisualElements.Wide310x150Logo = thumb;
                secondaryTile.VisualElements.ShowNameOnSquare150x150Logo = true;
                secondaryTile.VisualElements.ShowNameOnWide310x150Logo = true;
                secondaryTile.RoamingEnabled = true;
                this.TileExists = await secondaryTile.RequestCreateForSelectionAsync(rect, Windows.UI.Popups.Placement.Above);
                if (this.TileExists)
                {
                    TileManager.UpdateTile(this.ViewSource.ID, this.ViewSource.Unwatched.Count);
                }
                return;
            }
            else
            {
                SecondaryTile secondaryTile = new SecondaryTile(this.ViewSource.ID.ToString());
                this.TileExists = !(await secondaryTile.RequestDeleteForSelectionAsync(rect, Windows.UI.Popups.Placement.Above));
            }
        }

        private bool OnTileCanExecute(FrameworkElement e)
        {
            return this.ViewSource != null && this.ViewSource.Subscription;
        }

        private void OnUnAiredExecuted(ItemClickEventArgs e)
        {
            base.NavigationService.Navigate<AirViewModel>(0);
        }

        private bool OnUnAiredCanExecute(ItemClickEventArgs e)
        {
            return this.ViewSource != null && this.ViewSource.UnAired.Any();
        }

        private void OnActorExecuted(ItemClickEventArgs e)
        {
            base.NavigationService.Navigate<ActorViewModel>();
        }

        private bool OnActorCanExecute(ItemClickEventArgs e)
        {
            return this.ViewSource != null && this.ViewSource.Actors.Any();
        }

        private void OnLatestUnwatchedExecuted(ItemClickEventArgs e)
        {
            base.NavigationService.Navigate<EpisodeViewModel>(this.LatestUnwatched.ID);
        }

        private bool OnLatestUnwatchedCanExecute(ItemClickEventArgs e)
        {
            return this.ViewSource != null && this.ViewSource.Subscription && this.ViewSource.Unwatched.Any();
        }

        private void OnNextUnAiredExecuted(ItemClickEventArgs e)
        {
            base.NavigationService.Navigate<EpisodeViewModel>(this.NextUnAired.ID);
        }

        private bool OnNextUnAiredCanExecute(ItemClickEventArgs e)
        {
            return this.ViewSource != null && (this.LatestUnwatched == null || !this.ViewSource.Subscription) && this.ViewSource.UnAired.Any();
        }

        #endregion

        private void SelectedItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            this.SelectionClearCommand.NotifyCanExecuteChanged();
            this.WatchStateCommand.NotifyCanExecuteChanged();
            this.UnwatchStateCommand.NotifyCanExecuteChanged();
            base.NavigationBarIsOpen = this.SelectedItems.Any();
        }

        private void InvalidateCommands()
        {
            this.WatchStateCommand.NotifyCanExecuteChanged();
            this.UnwatchStateCommand.NotifyCanExecuteChanged();
        }

        private void LoadRandomFanArt()
        {            
            int max = this.ViewSource.FanArtBanners.Count - 1;
            if (max == -1)
            {
                return;
            }
            if (max > 10)
            {
                max = 10;
            }

            int rnd = new Random().Next(0, max);
            this.RandomFanArt = this.ViewSource.FanArtBanners[rnd];
        }

        public override void OnNavigatedTo(object navigationParameter)
        {
            base.OnNavigatedTo(navigationParameter);
            this.SelectedItems.Clear();

            this.ViewSource = AppX.Instance.Current;
            if (this.ViewSource != null)
            {
                this.View = new CollectionViewSource()
                {
                    Source = this.viewSource.Seasons
                }.View;
                base.OnPropertyChanged<ICollectionView>(() => this.View);

                this.TileExists = SecondaryTile.Exists(this.ViewSource.ID.ToString());
                LoadRandomFanArt();           
            }
        }

        public void UpdateTransientState(object state)
        {
            var viewState = (CollectionViewState)state;

            foreach (int id in viewState.SelectedItems)
            {
                var episode = this.ViewSource.Seasons.FirstOrDefault(E => E.ID == id);
                if (episode != null)
                {
                    this.SelectedItems.Add(episode);
                }
            }

            var currentItem = this.viewSource.Seasons.FirstOrDefault(E => E.ID == viewState.Position);
            if (currentItem != null)
            {
                this.View.MoveCurrentTo(currentItem);
            }
        }

        public object GetTransientState()
        {
            CollectionViewState state = new CollectionViewState();
            if (this.View != null && this.View.CurrentItem != null)
            {
                state.Position = (this.View.CurrentItem as SeasonModel).ID;
            }
            state.SelectedItems = this.SelectedItems.Select(S => S.ID).ToArray();
            return state;
        }

        public IEnumerable<Type> GetTransientStateTypes()
        {
            yield return typeof(CollectionViewState);
        }
    }
}
